#include <string.h>
#include "StoreObject.h"

namespace gitstore {



const StoreObject::String StoreObject::_null = "";

StoreObject::StoreObject()
    :_type(StoreObjectType::UnKnown) {

}

StoreObject::StoreObject(StoreObjectType type)
    :_type(type){
    switch ( _type ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
        _value.str = new String();
        break;
    case StoreObjectType::List:
        _value.list = new List();
        break;
    case StoreObjectType::Map:
        _value.map = new Map();
        break;
    case StoreObjectType::UnKnown:
        _value.str = nullptr;

    }
}

StoreObject::StoreObject(const StoreObject& object)
    :_type(object._type){
    switch ( _type ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
        _value.str = new String(*object._value.str);
        break;
    case StoreObjectType::List:
        _value.list = new List(*object._value.list);
        break;
    case StoreObjectType::Map:
        _value.map = new Map(*object._value.map);
        break;
    case StoreObjectType::UnKnown:
        break;
    }
}

StoreObject::StoreObject(StoreObject&& object)
    :_type(object._type){
    switch ( _type ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
    {
        _value.str = object._value.str;
        object._value.str = nullptr;
    }
        break;
    case StoreObjectType::List:
    {
        _value.list = object._value.list;
        object._value.list = nullptr;
    }
        break;
    case StoreObjectType::Map:
    {
        _value.map = object._value.map;
        object._value.map = nullptr;
    }
        break;
    case StoreObjectType::UnKnown:
        break;
    }
}

StoreObject& StoreObject::operator=(const StoreObject& object)
    {
    _type = object._type;
    switch ( _type ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
    {
        if (_value.str) {
            delete _value.str;
        }
        _value.str = new String(*object._value.str);
    }
        break;
    case StoreObjectType::List:
    {
        if (_value.list) {
            delete _value.list;
        }
        _value.list = new List(*object._value.list);
    }
        break;
    case StoreObjectType::Map:
    {
        if (_value.map) {
            delete _value.map;
        }
        _value.map = new Map(*object._value.map);
    }
        break;
    case StoreObjectType::UnKnown:
        break;
    }
    return *this;
}

StoreObject& StoreObject::operator=(StoreObject&& object) {
    _type = object._type;
    switch ( _type ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
    {
        _value.str = object._value.str;
        object._value.str = nullptr;
    }
        break;
    case StoreObjectType::List:
    {
        _value.list = object._value.list;
        object._value.list = nullptr;
    }
        break;
    case StoreObjectType::Map:
    {
        _value.map = object._value.map;
        object._value.map = nullptr;
    }
        break;
    case StoreObjectType::UnKnown:
        break;
    }
    return *this;
}

StoreObject::StoreObject(const char* cvalue)
    :_type(StoreObjectType::String){
    _value.str = new String(cvalue);
}

StoreObject::StoreObject(const char* cvalue, size_t size)
    :_type(StoreObjectType::Blob){
    _value.str = new String(cvalue, size);
}

StoreObject::StoreObject(const std::string &svalue, StoreObjectType type)
    :_type(type){
    _value.str = new String(svalue);
}

StoreObject::StoreObject(std::string &&svalue, StoreObjectType type)
    :_type(type){
    _value.str = new String(svalue);
}

StoreObject::StoreObject(const List& vec)
    :_type(StoreObjectType::List){
    _value.list = new List(vec);
}

StoreObject::StoreObject(List&& vec)
    :_type(StoreObjectType::List){
    _value.list = new List(forward<List>(vec));
}

StoreObject::StoreObject(const InitializerList& ilist)
    :_type(StoreObjectType::List){
    _value.list = new List(ilist);
}

StoreObject::StoreObject(const Map& mp)
    :_type(StoreObjectType::Map){
    _value.map = new Map(mp);
}

StoreObject::StoreObject(Map&& mp)
    :_type(StoreObjectType::Map){
    _value.map = new Map(forward<Map>(mp));
}

StoreObject::StoreObject(const InitializerMap& mlist)
    :_type(StoreObjectType::Map){
    _value.map = new Map(mlist);
}

StoreObject::~StoreObject() {
    switch ( _type ) {
    case StoreObjectType::String:
        releaseString();
        break;
    case StoreObjectType::Blob:
        releaseString();
        break;
    case StoreObjectType::List:
        releaseList();
        break;
    case StoreObjectType::Map:
        releaseMap();
        break;
    case StoreObjectType::UnKnown:
        break;
    }
}

inline void StoreObject::releaseString() {
    if ( _value.str ) {
        delete _value.str;
    }
}

inline void StoreObject::releaseBlob() {
    if ( _value.str ) {
        delete _value.str;
    }
}

inline void StoreObject::releaseMap() {
    if ( _value.map ) {
        delete _value.map;
    }
}

inline void StoreObject::releaseList() {
    if ( _value.list ) {
        delete _value.list;
    }
}

void StoreObject::resetType(StoreObjectType type) {
    switch ( _type ) {
    case StoreObjectType::String:
        releaseString();
        break;
    case StoreObjectType::Blob:
        releaseString();
        break;
    case StoreObjectType::List:
        releaseList();
        break;
    case StoreObjectType::Map:
        releaseMap();
        break;
    case StoreObjectType::UnKnown:
        break;
    }
    _type = type;
    switch ( _type ) {
    case StoreObjectType::String:
        _value.str = new String();
        break;
    case StoreObjectType::Blob:
        _value.str = new String();
        break;
    case StoreObjectType::List:
        _value.list = new List();
        break;
    case StoreObjectType::Map:
        _value.map = new Map();
        break;
    case StoreObjectType::UnKnown:
        break;
    }
}

bool StoreObject::isString() const {
    return _type == StoreObjectType::String;
}

bool StoreObject::isBlob() const {
    return _type == StoreObjectType::Blob;
}

bool StoreObject::isList() const {
    return _type == StoreObjectType::List;
}

bool StoreObject::isMap() const {
    return _type == StoreObjectType::Map;
}

StoreObject::String& StoreObject::operator [](size_t index) {
    if ( _type != StoreObjectType::List ) {
        throw ErrTypeNotMatch();
    }
    return (*_value.list)[index];
}

const StoreObject::String& StoreObject::operator [](size_t index) const {
    if ( _type != StoreObjectType::List ) {
        return _null;
    }
    return (*_value.list)[index];
}

StoreObject::String& StoreObject::operator [](const String& key) {
    if ( _type != StoreObjectType::Map ) {
        throw ErrTypeNotMatch();
    }
    return (*_value.map)[key];
}

const StoreObject::String& StoreObject::operator [](const String& key) const {
    if ( _type != StoreObjectType::Map ) {
        return _null;
    }
    return (*_value.map)[key];
}

bool StoreObject::isKeyExist(const String& key) const {
    if ( _type != StoreObjectType::Map ) {
        return false;
    }
    if ( !_value.map ) {
        return false;
    }
    return _value.map->find(key) != _value.map->end();
}

StoreObjectType StoreObject::getType() const {
    return _type;
}

StoreObject::MapIterator StoreObject::mbegin() const {
    if ( _type == StoreObjectType::Map )
        return _value.map->begin();
    else {
        throw ErrTypeNotMatch();
    }
}

StoreObject::MapIterator StoreObject::mend() const {
    if ( _type == StoreObjectType::Map )
        return _value.map->end();
    else {
        throw ErrTypeNotMatch();
    }
}

StoreObject::ListIterator StoreObject::vbegin() const {
    if ( _type == StoreObjectType::List )
        return _value.list->begin();
    else {
        throw ErrTypeNotMatch();
    }
}

StoreObject::ListIterator StoreObject::vend() const {
    if ( _type == StoreObjectType::List )
        return _value.list->end();
    else {
        throw ErrTypeNotMatch();
    }
}

void StoreObject::clear() {
    switch ( _type ) {
    case StoreObjectType::String:
        _value.str->clear();
        break;
    case StoreObjectType::Blob:
        _value.str->clear();
        break;
    case StoreObjectType::List:
        _value.list->clear();
        break;
    case StoreObjectType::Map:
        _value.map->clear();
        break;
    case StoreObjectType::UnKnown:
        break;
    }
}

size_t StoreObject::size() const {
    switch ( _type ) {
    case StoreObjectType::Blob:
    case StoreObjectType::String:
        return _value.str->size();
    case StoreObjectType::List:
        return _value.list->size();
    case StoreObjectType::Map:
        return _value.map->size();
    case StoreObjectType::UnKnown:
        return 0;
    }
}

StoreObject::String StoreObject::get(size_t index) const{
    if ( _type != StoreObjectType::List ) {
        return "";
    }
    return (*_value.list)[index];
}

StoreObject::String StoreObject::get(const String& key) const {
    if ( _type != StoreObjectType::Map ) {
        return "";
    }
    return (*_value.map)[key];
}

StoreObject::String StoreObject::get() const{
    if ( _type != StoreObjectType::String || _type != StoreObjectType::Blob ) {
        return "";
    }
    return (*_value.str);
}

StoreObject::String StoreObject::at(size_t index) const{
    if ( _type != StoreObjectType::List ) {
        return "";
    }
    return (*_value.list)[index];
}

StoreObject::String StoreObject::at(const String& key) const {
    if ( _type != StoreObjectType::Map ) {
        return "";
    }
    return (*_value.map)[key];
}

StoreObject::String StoreObject::at() const{
    if ( _type != StoreObjectType::String || _type != StoreObjectType::Blob ) {
        return "";
    }
    return (*_value.str);
}

void StoreObject::set(const char* cvalue) {
    if ( _type == StoreObjectType::String ) {
        _value.str->assign(cvalue);
    }
}

void StoreObject::set(const char* cvalue, size_t size) {
    if ( _type == StoreObjectType::Blob ) {
        _value.str->assign(cvalue, size);
    }
}

void StoreObject::set(const std::string &svalue) {
    if ( _type == StoreObjectType::String || _type == StoreObjectType::Blob ) {
        _value.str->assign(svalue);
    }
}

void StoreObject::set(std::string&& svalue) {
    if ( _type == StoreObjectType::String || _type == StoreObjectType::Blob ) {
        _value.str->assign(svalue);
    }
}

StoreObject::String StoreObject::getSerializedValue() const {
    std::ostringstream ss;
    boost::archive::binary_oarchive ob(ss);
    ob << static_cast<int>(_type);
    switch (_type) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
        ob << (*_value.str);
        break;
    case StoreObjectType::List:
        ob << (*_value.list);
        break;
    case StoreObjectType::Map:
        ob << (*_value.map);
        break;
    }
    return ss.str();
}

void StoreObject::fromSerializedValue(const String& value) {
    std::istringstream ss(value);
    boost::archive::binary_iarchive ob(ss);
    ob >> _type;
    switch ( _type ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
        ob >> (*_value.str);
        break;
    case StoreObjectType::List:
        ob >> (*_value.list);
        break;
    case StoreObjectType::Map:
        ob >> (*_value.map);
        break;
    }
}

void StoreObject::fromSerializedValue(String&& value) {
    std::istringstream ss(std::forward<String>(value));
    boost::archive::binary_iarchive ob(ss);
    ob >> _type;
    switch ( _type ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
        ob >> (*_value.str);
        break;
    case StoreObjectType::List:
        ob >> (*_value.list);
        break;
    case StoreObjectType::Map:
        ob >> (*_value.map);
        break;
    }
}

}
